<?php
/**
 * 应用路由引导，通过解析URL定位到控制器目录的业务方法
 * @since 2014-05-14 
 * @author leicc<355108@nd.com>
 */
namespace classlib\core;
use classlib\core\config;
class router {
 	/**
 	 * 定义url规则参数最长长度
 	 * @var string
 	 */
	const URI_MAX_LEN = 512;
	
	/**
	 * 定义控制器逻辑的前缀
	 * @var string
	 */
	const CTRL_PREDIX = 'C';
	
	/**
	 * 定义解析之后uri信息
	 * @var string
	 */
	static $uri = '';
	
	/**
	 * 定义路由引导找文件、类、方法
	 * @var array
	 * @staticvar
	 */
	public static $app = array(
		'class' 	=> 'index', 
		'method'	=> 'main',
		'controller'=> null
	);
	
	/**
	 * 定义系统回调函数资源释放器
	 * @var array callable
	 * @staticvar
	 */
	public static $hooks = array();
	
	/**
	 * 获取请求的domain资料信息
	 * @return string $domain
	 */
	public static function base_url() {
		static $base_uri = '';
		if (!$base_uri) {
			$proto    = ((isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'])
				|| (isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == 443))? 'https://':'http://';
			$base_uri = $proto.$_SERVER['SERVER_NAME'];
		}
		return $base_uri;
	}
	
	/**
	 * 判断一个请求是否是ajax请求
	 * @return bool
	 */
	public static function isajax() {
		if(isset($_SERVER['HTTP_X_REQUESTED_WITH'])
			&& strcasecmp($_SERVER['HTTP_X_REQUESTED_WITH'], 'xmlhttprequest') == 0) {
			return true;
		}
		return false;
	}
	
	/**
	 * 注册资源结束释放函数到对接中
	 * @param callable $callback
	 */
	public static function register($callback) {
		array_push(self::$hooks, $callback);
	}
	
	/**
	 * 生成一个认证签名
	 * @param string $script
	 * @return string
	 */
	public static function hash($script) {
		$randstr = 'SDAFS#$DFG%#@DFDSGDSG&*^&*FDS';
		$hash    = substr(md5($script.$randstr), 0, 6);
		return $hash;
	}
	
	/**
	 * 格式化输入css、js
	 * @param array $script a;b.js
	 * @param string $type js | css
	 * @return string
	 */
	public static function minify($script, $type='js') {
		$script = self::hash($script).','.$script;
		$vrand  = mt_rand(1000, 9999);
		$url    = self::get_url('minify', $type, $script, '.'.$type).'?v='.$vrand;
		return $url;
	}
	
	/**
	 * 构建URL规则, 让系统URL跳转做到自适应
	 * @access public
	 * @param string $class    业务controller类的相关路径
	 * @param string $method   业务执行方法
	 * @param string $segments 业务追加的参数
	 * @param string $subfix   生成URL的后缀  
	 * @return string $surl    返回生成的url资源地址
	 */
    public static function get_url($class='', $method='', $segments='', $subfix='.html') {
        $sjoin = '-';
		$sglue = '/';
     	$surl  = rtrim('/'.str_replace('-', '/', $class), '/');
		if($method != '') {
			$surl .= '/'.$method;
		}
        //拼接业务GET请求参数
        if(is_array($segments)) {
            foreach($segments as $key=>$val) {
                $surl .= $sglue.$key.$sjoin.(preg_match('/[\w,\-]{1,}/', $val)? $val:rawurlencode($val));
            }
        } else if(is_string($segments) && $segments != '') {
			$surl .= $sglue.str_replace(array('&', '='), array($sglue, $sjoin), $segments);            
        }
		$surl .= $subfix;
		//验证端口，如果是默认端口直接跳过
		if ($_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443) {
			$surl  = self::base_url().':'.$_SERVER['SERVER_PORT'].$surl;
		}
        return $surl;
    }
	 
    /**
     * 执行业务的初始化操作
     * @static
     * @return void
     */
    public static function __init__() {
    	ob_start();
    	self::$uri = isset($_SERVER['REQUEST_URI'])? $_SERVER['REQUEST_URI']:$_SERVER['PATH_INFO'];
    	//舍弃扫描脚本，直接掉过不处理过程的URI业务
    	if (strlen(self::$uri) > self::URI_MAX_LEN) {
    		\classlib\core\do_exit(500);
    	}
    	self::$uri = parse_url(self::$uri, PHP_URL_PATH);
    	$filter    = array('/\.+/', '/[\.|\?][^\.]+?$/', '/^[\w\/]+\.php[\?]?/', '/[\/]+/');
    	$replace   = array('.', '', '', '/');
    	self::$uri = preg_replace($filter, $replace, self::$uri);
    	self::$uri = ltrim(self::$uri, '/');
    	$asegms    = (self::$uri)? explode('/', self::$uri):array();
    	//请求的第一段作为 应用基础空间 --下划线开头的空间不允许访问
    	if ($asegms) {
    		if (is_dir(COREDIR.'/src/_'.$asegms[0])) {//APP目录结构使用下划线开头
    			config::$appdir = COREDIR.'/src/_'.$asegms[0];
    			array_shift($asegms);
    		}
    	}
    	(!config::$appdir) && config::$appdir = COREDIR.'/src/_app';
    	//设置工作的地理时区 等信息
    	$config = config::load('config', false);
    	$tz     = isset($_COOKIE['tz'])? $_COOKIE['tz']:$config['datezone'];
    	if(!date_default_timezone_set($tz)) {
    		date_default_timezone_set($config['datezone']);
    	}
		config::$nowtime = isset($_SERVER['REQUEST_TIME'])? $_SERVER['REQUEST_TIME']:time();
		model::set_cacher(\classlib\cache::inst($config['cache']));
		return $asegms;
    }
    
    /**
     * 释放资料例如数据连接
     * @static
     * @return void
     */
	public static function __destroy() {
		while (($caller = array_shift(self::$hooks))) {
			call_user_func($caller);
		}
	}
    
    /**
     * 执行程序业务引导，需要先调用业务路由引导
     * 程序业务引导的关键，其导向到控制器目录
     * @static
     */
    public static function __run($asegms) {
		$file   = config::$appdir.'/controller/';
		$class  = '';//解析加载的类文件
		while($ssegm = array_shift($asegms)) {
			if(!$ssegm) {
				continue;
			}
			$file .= $ssegm;
			if (file_exists($file.'.do.php')) {
				$class = $ssegm;
				$file .= '.do.php';
				break;
			} elseif (is_dir($file)) {
				$file .= '/';
				continue;
			} else {
				$file  = dirname($file).'/';
				array_unshift($asegms, $ssegm);
				break;
			}
		}
		//加载默认的class文件
		if (!$class) {
			$class = self::$app['class'];
			$file .= self::$app['class'].'.do.php';
		}
		(!is_file($file)) && \classlib\core\do_exit(404);
		//获取要执行的controller方法名称
		$method = array_shift($asegms);
		if ($method && (is_numeric($method) || !preg_match('/^[a-z0-9_]{2,}$/i', $method))) {
			array_unshift($asegms, $method);
			$method = '';
		}
		//初始化传递的参数资料信息
		while($ssegm = array_shift($asegms)) {
			if (strpos($ssegm, '-') === FALSE) {
				$_GET[]   = $ssegm;
			} else {
				list($k, $v) = explode('-', $ssegm, 2);
				$_GET[$k] = $v;
			}
		}
        require $file;
        //方法名字是否已经获取了
        if ($method && $method != self::$app['method']) {
        	self::$app['method'] = $method;
        }
        $class = 'src\\'.self::CTRL_PREDIX.$class;
        self::$app['controller'] = new $class();
    }
}